package com.foruo.algo.bucket;

import java.util.concurrent.TimeUnit;

/**
 * 令牌桶算法
 *
 * 保护当前服务，支持突发速率
 *
 * @author GaoYuan
 * @date 2018/10/25 上午10:55
 */
public class BucketTokenAlgo {

    /** 每秒10个令牌 */
    static long passNumPerSecond = 10;

    /** 桶令牌的最大值 */
    static long bucketNumMax = 100;

    /** 当前桶的已用掉的令牌数量 */
    static volatile long bucketNumCurrent = 0;

    /** 最后一次计算的时间 */
    static volatile long lastTime = System.currentTimeMillis();

    /**
     * 获取
     * false 表示 令牌用完了
     * true 表示成功获取令牌
     * @author GaoYuan
     * @date 2018/10/25 上午11:06
     */
    public static boolean tryAquire(){
        // 计算目前的数据量
        populateBucket();
        if(bucketNumCurrent >= bucketNumMax){
            // 令牌用完了
            return false;
        }
        bucketNumCurrent ++;
        return true;
    }

    /**
     * 计算当前令牌桶的令牌情况
     * @author GaoYuan
     * @date 2018/10/25 上午11:08
     */
    public static void populateBucket(){
        // 计算距离上次获取令牌，过去了多久
        long useTime = System.currentTimeMillis() - lastTime;
        // 计算应该需要恢复多少令牌
        long shouldReleaseNum = (useTime * passNumPerSecond)/1000;
        if((bucketNumCurrent -= shouldReleaseNum) < 0){
            bucketNumCurrent = 0;
        }
        // 修改最后计算时间
        lastTime = System.currentTimeMillis();
    }

    public static class MyThread extends Thread{

        private int i;

        public MyThread(int i){
            this.i = i;
        }

        @Override
        public void run() {
            try{
                long nowTm = System.currentTimeMillis();
                if(tryAquire()){
                    System.out.println("i = "+ i +"，" + Thread.currentThread().getName() + "执行成功了，" + nowTm);
                }else{
                    System.err.println("i = "+ i +"，" + Thread.currentThread().getName() + "执行失败了，" + nowTm);
                }
            }catch (Exception e){}
        }

        public int getI() {
            return i;
        }

        public void setI(int i) {
            this.i = i;
        }

    }


    public static void main(String[] args) throws Exception{
        // 每秒 20 请求
        int index = 0;
        while (true) {
            TimeUnit.SECONDS.sleep(1);
            System.out.println("第" + index ++ + "轮");
            for (int i = 0; i < 20; i++) {
                new MyThread(i).start();
            }
        }
    }
    
}
